﻿using System;
using System.Collections.Generic;
using System.Text;

namespace LookAHead
{
    //cell is a cordinate in the grid. handle all function in the zone
    class Cell : IComparable
    {
        public int x; //the row cordinate of cell
        public int y;//the coulmn cordinate of cell
        public double h;//the hurstic cell value of this cell to target goal
        public double g;//the row cordinate of cell
        public double fu;// f ufdated value
        public bool InBranch = false;//boll parm for knowledge if the path is in 
        public int IndexInHeap = -1; //will conatin the index in the heap
        public double HInBranch = -1; //will conatin the h updated in branch in lk lookahead
        public enum states { empty, obstacle, goal, start };
        public states state;
        /*
         *  0  - empty
         *  1  - obstacle
         *  2  - goal
         *  3  - start
         * */
        public static double SQRT_2 = Math.Sqrt(2);
        public enum status { none, close, open };
        public bool BeenExpanded = false;
        public bool MrecRemmber = false;
        public status hash_s;
        public Cell parent; //pointer to the cell parent
        /********************************************************************************/
        public Cell(Cell Other)
        //copy constructor
        {
            this.x = Other.x;
            this.y = Other.y;
            this.h = Other.h;
            this.g = Other.g;
            this.fu = Other.fu;
            this.hash_s = Other.hash_s;
            this.BeenExpanded = Other.BeenExpanded;
            this.MrecRemmber = Other.MrecRemmber;
            this.state = Other.state;
            if (Other.parent == null)
                this.parent = null;
            else
                this.parent = new Cell(Other.parent);
            this.InBranch = false;
            IndexInHeap = Other.IndexInHeap;
        }
        /********************************************************************************/
        public Cell(int x_, int y_)
        //constructor for a regular cell
        {
            this.x = x_;
            this.y = y_;
            h = 0;
            g = 0;
            fu = h;
            state = 0;
            BeenExpanded = false;
            MrecRemmber = false;
            parent = null;
            hash_s = status.none;
            this.InBranch = false;
            IndexInHeap = -1;
        }
        /********************************************************************************/
        public void Clear()
        //init the cell
        {
            g = 0;
            fu = h;
            InBranch = false;
            parent = null;
            hash_s = status.none;
            BeenExpanded = false;
            MrecRemmber = false;
        }
        /********************************************************************************/
        public int CompareTo(object obj)
        {
            Cell Other = obj as Cell; // avoid double casting
            if (Other == null)
            {
                throw new ArgumentException("A Cell object is required for comparison.", "obj");
            }
            return this.CompareTo(Other);
        }
        /********************************************************************************/
        public int CompareTo(Cell Other)
        {
            double f1, f2;
            if (program.CurrentAlgo == program.UniformCostSearchNum)
                return (this.g.CompareTo(Other.g));
            else if ((program.CurrentAlgo == program.LookAHeadNum) || (program.CurrentAlgo == program.LookAHeadWithBpmxNum))
            {
                f1 = this.fu;
                f2 = Other.fu;
            }
            else
            { //other algo's
                f1 = this.g + this.h;
                f2 = Other.g + Other.h;
            }
            if (f1 == f2) //start comapring
                return (this.h.CompareTo(Other.h));
            if (f1 > f2)
                return 1;
            return -1; //else
        }
        /********************************************************************************/
        public bool IsEqual(Cell Other)
        //equal between me and other
        {
            if (Other == null)
                return false;
            bool ans = ((this.x == Other.x) && (this.y == Other.y));
            return ans;
        }
        /********************************************************************************/
        public bool IsEmpty()
        //is this cell is an empty one?
        {
            return ((this.state == states.empty) || (this.state == states.start) || (this.state == states.goal));
        }
        /********************************************************************************/
        public bool CreateObstacle()
        //tring to change this cell to an obstacle cell
        {
            if (this.state == states.empty)
            {
                this.state = states.obstacle;
                return true;
            }
            else
                return false;
        }
        /********************************************************************************/
        public bool ChangeToGoal()
        //changing this cell to be the goal cell
        {
            this.state = states.goal;
            return true;
        }
        /********************************************************************************/
        public bool ChangeToOpen()
        //moving cell to the openlist
        {
            if (status.none == hash_s)
            {
                if ((!program.AncestorDetection) || (program.AncestorDetection && program.MrecDetection))
                    program.memory++;
                this.hash_s = status.open;
                program.Genrated++;
                return true;
            }
            program.ReGenrated++; //close or open
            this.hash_s = status.open; //close become open
            return true;
        }
        /********************************************************************************/
        public bool ChangeToNone()
        //changing cell to non in the lists
        {
            program.memory--;
            this.hash_s = status.none;
            return true;
        }
        /********************************************************************************/
        public bool ChangeToClose()
        //changing cell to close
        {
            this.hash_s = status.close;
            return true;
        }
        /********************************************************************************/
        public bool ChangeToStart()
        //statring the cell to be the start cell of grid
        {
            this.state = states.start;
            return true;
        }
        /********************************************************************************/
        public bool ChangeToEmpty()
        //emptying cell
        {
            this.state = 0;
            return true;
        }
        /********************************************************************************/
        public double GetDistance(Cell Other)
        //calculating the distance between me and my parent will reutrn 1 or sqrt(2)
        {
            double ans = 0;
            ans = ans + Math.Abs(this.x - Other.x);
            ans = ans + Math.Abs(this.y - Other.y);
            ans = Math.Sqrt(ans);
            return (ans);
        }
        /********************************************************************************/
        public int GetDepth()
        //return the depth of the cell
        {
            int ans = 0;
            Cell parent = this.parent;
            while (parent != null)
            {
                parent = parent.parent;
                ans++;
            }
            return (ans);
        }
        /********************************************************************************/
        public bool CouldGenrate(Cell p)
        //trying to genrate me with parent p
        {
            if (program.AncestorDetection)
                return GenrateAD(p);
            else
                return GenrateRG(p);
        }
        /********************************************************************************/
        public bool GenrateAD(Cell parent)
        //genrating this with parent p in the lookahead function
        {
            if (InBranch) //in ancestor - i don't want to repet my branch
                return false;
            if ((program.CurrentAlgo == program.LookAHeadNum) || (program.CurrentAlgo == program.LookAHeadWithBpmxNum))
                program.LookaHeadVisited++;
            else
            { //dfs or ida* or mrec
                double NewG = parent.g + GetDistance(parent);
                if ((program.CurrentAlgo == program.MrecWithBpmxNum || program.CurrentAlgo == program.MrecNum) && (this.MrecRemmber) && (MyMath.Greater(NewG, g)))
                    return false; //i found already a better way
                this.parent = parent;
                this.g = NewG;
            }//dfs
            return true;
        }
        /********************************************************************************/
        public bool GenrateRG(Cell parent) //p will be my new parent
        //genrating "this" with parent p in any function diff from lookahead 
        {
            double NewG = parent.g + GetDistance(parent); //the new g from this parent p
            if (hash_s != status.none)
                if (MyMath.GreaterOrEqual(NewG, this.g))//if (new_g >= this.g)
                    return false; //my pre parent was better
            this.parent = parent;
            this.g = NewG;
            return true;
        }
        /********************************************************************************/
        public void expand()
        //function will expand "this", marking it as expand
        //the function is for data collection  only and not necessity for algoritem
        {

            if (!BeenExpanded)
            {//my first time expanding it this
                program.expanded++;
                BeenExpanded = true;
            }
            else
                program.Reexpanded++; // This currently does not happen in bfs but will happen in ida* and mrec
            ChangeToClose();
        }
        /********************************************************************************/
        public void CalcH(Cell c)
        //calculating hurstic for Cell c, octile distance from Cell c to goal
        {
            int delta_x = Math.Abs(this.x - c.x);
            int delta_y = Math.Abs(this.y - c.y);
            double min = Math.Min(delta_x, delta_y);
            this.h = (SQRT_2 * min) + Math.Abs(delta_x - delta_y);
            if (program.WithBpmx)
                if (!(program.CurrentAlgo == program.LookAHeadWithBpmxNum && program.AncestorDetection))
                    this.h = CalcHFromMBH(c);
            this.fu = Math.Max(fu, g + h);
        }
        /********************************************************************************/
        public double CalcHFromMBH(Cell c) //calc h from memory bases huristic
        //calculating hurstic for Cell c, octile distance from Cell c to goal using pattern database
        {
            double ansH = this.h;
            int rand;//= (this.x+this.y)%6;
            double newH;
            for (int i = 0; i < program.GridBoard.GridMBH.Length * 0.3; i++)
            {
                rand = program.random.Next(0, program.GridBoard.GridMBH.Length);
                newH = Math.Abs(program.GridBoard.GridMBH[rand].PdbCost[this.x, this.y] - program.GridBoard.GridMBH[rand].PdbCost[c.x, c.y]);
                if (MyMath.Greater(newH, ansH))
                    ansH = newH;
                //rand = rand + 1;
            }
            return ansH;

        }
        /********************************************************************************/
    }
}

